
//--xuezhen--

`include "defines.v"

module exe_stage(
  //input wire clk,  //added in 0407
  input wire rst,
  input wire [7 : 0]alu_opcode,
  input wire alu_single_word,
  input wire [`REG_BUS]op1,
  input wire [`REG_BUS]op2,
  input wire [2:0]branch_type,
  input wire [1:0]jump_type,    //00: not jump   10:JAL   11:JALR
  input wire [`REG_BUS]pc_i,     //used in exe stage 

  //pass
  input wire [4 : 0]inst_type_i,
  input wire rd_w_ena_i,
  input wire [4 : 0]rd_w_addr_i,
  input wire l_mem_i,
  input wire s_mem_i,
  input wire [1:0]datasize_i,
  input wire unsigned_load_i,
  input wire [`REG_BUS]rs1_data_i,
  input wire [`REG_BUS]rs2_data_i,
  input wire [31:0]inst_i,
  input wire [11:0]csr_addr_i,
  input wire csr_ena_i,
  input wire [`REG_BUS]csr_op_i,
  input wire [1:0]csr_mode_i,
  input wire putch_en_i,
  input wire [7:0]a0_i,
  input wire ecall_i,
  input wire mret_i,
  //input wire time_intr_i,
  input wire time_intr_firstdetect_i,



  //result
  output reg  [`REG_BUS]alu_result_o,
  output reg jump,    //flush to pc!
  output reg [`REG_BUS]pc_dest,
  //output wire [`REG_BUS]next_pc,



  //pass
  output wire [4 : 0]inst_type_o,
  output wire rd_w_ena_o,
  output wire [4 : 0]rd_w_addr_o,
  output wire l_mem_o,
  output wire s_mem_o,
  output wire [1:0]datasize_o,
  output wire unsigned_load_o,
  output wire [`REG_BUS]rs2_data_o,
  output wire [31:0]inst_o,
  output wire [`REG_BUS]pc_o,
  output wire [11:0]csr_addr_o,
  output wire csr_ena_o,
  output wire [`REG_BUS]csr_op_o,
  output wire [1:0]csr_mode_o,
  output wire putch_en_o,
  output wire [7:0]a0_o,
  output wire ecall_o,
  output wire mret_o,
  //output wire time_intr_o,
  output wire time_intr_firstdetect_o
);

assign inst_type_o = inst_type_i;
assign inst_o = inst_i;
assign pc_o = pc_i;
assign putch_en_o = putch_en_i;
assign a0_o = a0_i;
assign ecall_o= ecall_i;
assign mret_o= mret_i;
//assign time_intr_o = time_intr_i;
assign time_intr_firstdetect_o = time_intr_firstdetect_i;

//to be used in mem stage
assign l_mem_o = l_mem_i;
assign s_mem_o = s_mem_i;
assign datasize_o = datasize_i;
assign unsigned_load_o = unsigned_load_i;
assign rs2_data_o = rs2_data_i;

//to be used in wb stage
assign rd_w_ena_o = rd_w_ena_i;
assign rd_w_addr_o = rd_w_addr_i;

assign csr_addr_o = csr_addr_i;
assign csr_ena_o = csr_ena_i;
assign csr_op_o = csr_op_i;
assign csr_mode_o = csr_mode_i;



/*
always @(posedge clk)
begin
  inst_type_o <= inst_type_i;
end
*/
reg [`REG_BUS]alu_result;
wire signed [`REG_BUS]signed_op1;
wire signed [`REG_BUS]signed_op2;
assign signed_op1 = op1;
assign signed_op2 = op2;

always@(*)  //originally *, changed in 0407
begin
  /*
  if( rst == 1'b1 ) begin
    alu_result = `ZERO_WORD;
  end
  else begin*/
  case( alu_opcode )
	  `ALU_ADD: begin 
      alu_result = op1 + op2;  
    end
    `ALU_SUB: begin
      alu_result = op1 - op2;
    end
    `ALU_XOR: begin
      alu_result = op1 ^ op2;
    end
    `ALU_OR : begin
      alu_result = op1 | op2;
    end
    `ALU_AND: begin
      alu_result = op1 & op2;
    end
    `ALU_SLT: begin
      alu_result[63:1] = {63{1'b0}};
      alu_result[0] = signed_op1 < signed_op2;
    end
    `ALU_SLTU: begin
      alu_result[63:1] = {63{1'b0}};
      alu_result[0] = op1 < op2;
    end
    `ALU_SLL: begin
      alu_result = op1 << op2[5:0];
    end
    `ALU_SRL: begin
      alu_result = op1 >> op2[5:0];
    end
    `ALU_SRA: begin
      alu_result = signed_op1 >>> op2[5:0];   //not signed_op2!!!
    end

	  default:   begin alu_result = `ZERO_WORD; end
	endcase
  //end
end


wire signed [`REG_BUS]signed_rs1_data_i;
wire signed [`REG_BUS]signed_rs2_data_i;
assign signed_rs1_data_i = rs1_data_i;
assign signed_rs2_data_i = rs2_data_i;

reg branch_condition;
always @(*) begin
  /*
  if( rst == 1'b1 ) begin
    branch_condition = 1'b0;
  end
  else begin*/
    //branch_condition = 1'b0;
    case(branch_type)
      `B_EQ: begin
        branch_condition = (signed_rs1_data_i == signed_rs2_data_i);
      end
      `B_NE: begin
        branch_condition = (signed_rs1_data_i != signed_rs2_data_i);
      end
      `B_LT: begin
        branch_condition = (signed_rs1_data_i < signed_rs2_data_i);
      end
      `B_GE: begin
        branch_condition = (signed_rs1_data_i >= signed_rs2_data_i);
      end
      `B_LTU: begin
        branch_condition = (rs1_data_i < rs2_data_i);
      end
      `B_GEU: begin
        branch_condition = (rs1_data_i >= rs2_data_i);
      end
      3'b000: begin    //not branch inst
        branch_condition = 1'b0;
      end
      default: branch_condition = 1'b0;
    endcase
  //end

end

//output pc_dest
always @(*) begin
  if ((ecall_i == 1'b1)||(mret_i == 1'b1)||(time_intr_firstdetect_i==1'b1)) begin
    pc_dest = `ZERO_WORD;
    jump = 1'b1;
  end
  else begin
    if (jump_type == 2'b10) begin
      pc_dest = alu_result;
      jump = 1'b1;
    end
    else if(jump_type == 2'b11) begin
      pc_dest = alu_result & ~1;
      jump = 1'b1;
    end
    else begin
      if (branch_condition == 1'b1) begin
        pc_dest = alu_result;
        jump = 1'b1;
      end
      else begin
        pc_dest = `ZERO_WORD;
        jump = 1'b0;
      end
    end
  end
end

/*
//next pc if no interruption
always @(*) begin
  if (jump_type == 2'b10) begin
    next_pc = alu_result;
  end
  else if(jump_type == 2'b11) begin
    next_pc = alu_result & ~1;
  end
  else begin
    if (branch_condition == 1'b1) begin
      next_pc = alu_result;
    end
    else begin
      next_pc = pc_4;
    end
  end
end
*/

//output single word or double words to mem_stage? If single, ext with sign; if jump inst, output pc+4 
wire [`REG_BUS]pc_4;
assign pc_4 = pc_i + 4;
always @(*) begin
  if ((jump_type == 2'b10)||(jump_type == 2'b11)) begin
    alu_result_o = pc_4;
  end
  else if (alu_single_word == 1'b1) begin
    alu_result_o = { {32{alu_result[31]}}, alu_result[31:0] };
  end
  else begin
    alu_result_o = alu_result;
  end
end




endmodule
